import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;

public class TestBinaryTree {
    static class TreeNode {
        public char val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(char val) {
            this.val = val;
        }
    }

    public TreeNode root;

    public void create() {
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        root = A;
        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        E.left = G;
        F.left = H;
    }

    // 前序遍历
    public void preOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        //先打印根结点，再遍历左右子树
        System.out.print(root.val);
        preOrder(root.left);
        preOrder(root.right);
    }

    // 非递归实现 前序遍历
    // 1
    public void preOrderNor(TreeNode root) {
        if (root == null) {
            return;
        }
        // 创建一个栈
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                System.out.print(cur.val);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            cur = top.right;
        }
        System.out.println();
    }
    // 2
    public void preOrderNor1(TreeNode root) {
        if (root == null) {
            return;
        }
        // 创建一个栈
        Deque<TreeNode> stack = new LinkedList<>();
        // 将根结点放入栈中
        stack.push(root);

        // 栈不空
        while (!stack.isEmpty()) {
            // 弹出栈顶结点
            TreeNode cur = stack.pop();
            // 打印弹出的结点
            System.out.print(cur.val);
            // 将当前结点的左右孩子放入栈中
            // 先放右孩子
            if (cur.right != null) {
                stack.push(cur.right);
            }
            if (cur.left != null) {
                stack.push(cur.left);
            }
        }
        System.out.println();
    }

    // 中序遍历
    public void inOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.val);
        inOrder(root.right);
    }

    // 非递归 中序遍历
    public void inOrderNor(TreeNode root) {
        if (root == null) {
            return;
        }
        // 创建一个栈
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            System.out.print(top.val);
            cur = top.right;
        }
        System.out.println();
    }

    // 后序遍历
    public void postOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val);
    }

    // 非递归 后序遍历
    public void postOrderNor(TreeNode root) {
        if (root == null) {
            return;
        }
        // 创建一个栈
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        // 记录之前已经弹出并打印的结点
        TreeNode prev = null;

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            // 如果栈顶结点，的右边为空，就弹出并打印
            if (top.right == null || top.right == prev) {
                System.out.print(top.val);
                prev = stack.pop();
            } else {
                cur = top.right;
            }
        }
        System.out.println();
    }


    // 获取树中节点的个数
    // 子问题思想
    public int size (TreeNode root) {
        if (root == null) {
            return 0;
        }

        // 左子树结点树 + 右子树结点树 + 1
        int leftTree = size(root.left);
        int rightTree = size(root.right);

        return leftTree + rightTree + 1;
    }

    // 遍历思想
    public int nodeSize;
    public void size2 (TreeNode root) {
        if (root == null) {
            return;
        }
        nodeSize++;
        size2(root.left);
        size2(root.right);
    }

    // 获取树中叶子结点个数
    public int getLeafNodeCount (TreeNode root) {
        if (root == null) {
            return 0;
        }
        //左右孩子为空 就是叶子结点 返回1
        if (root.left == null && root.right == null) {
            return 1;
        }

        return getLeafNodeCount(root.left) + getLeafNodeCount(root.right);
    }

    // 获取第K层节点的个数
    public int getKLevelNodeCount(TreeNode root,int k) {
        if (root == null) {
            return 0;
        }
        if (k == 1) {
            //找到要获取的那一层
            return 1;
        }

        return getKLevelNodeCount(root.left,k-1) + getKLevelNodeCount(root.right,k-1);
    }

    // 获取二叉树的高度
    // 时间复杂度 O(n)    空间复杂度 O(树的高度)
    public int getHeight(TreeNode root) {
        if (root == null) {
            return 0;
        }

        // 比较左右子树的高度
        // 返回 子树高度 + 1
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
            int height = leftHeight > rightHeight ? leftHeight : rightHeight;

        return height + 1;
    }



    // 检测值为value的元素是否存在
    // 遍历二叉树，找val
    public TreeNode find(TreeNode root, char val) {
        if (root == null) {
            return null;
        }
        if (root.val == val) {
            return root;
        }
        // 找左子树
        TreeNode left = find(root.left, val);
        if (left != null) {
            return left;
        }
        // 找右子树
        return find(root.right, val);
    }



    // 层序遍历
    public void levelOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        // 创建一个队列
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);// 根结点入队

        // 队列不空
        while (!queue.isEmpty()) {
            // 队列里弹出一个结点， 并打印
            TreeNode cur = queue.poll();
            System.out.print(cur.val + " ");
            // 将弹出结点的 左右孩子入队
            if (cur.left != null) {
                queue.offer(cur.left);
            }
            if (cur.right != null) {
                queue.offer(cur.right);
            }
        }
    }


    // 判断一棵树是不是完全二叉树
    public boolean isCompleteTree(TreeNode root) {
        if (root == null) {
            return false;
        }
        // 创建一个队列
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);// 根结点入队

        // 队首为null结束循环
        while (queue.peek() != null) {
            // 队列弹出一个结点
            TreeNode cur = queue.poll();
            // 不管该结点左右孩子是否为空，都放入队列
            queue.offer(cur.left);
            queue.offer(cur.right);
        }

        // 队首为null
        // 队列里面有不为null的元素，就不是完全二叉树
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            if (cur != null) {
                return false;
            }
        }

        return true;
    }




    // 练习题

    // 检查两颗树是否相同
    // 时间复杂度 0(min(m, n))   空间复杂度 0(min(min(m, n)))
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p != null && q == null || p == null && q != null) {
            //一个空，一个不空，结构不同，false
            return false;
        }
        if (p == null && q == null) {
            //两个都空
            return true;
        }

        // 两个都不空
        // 根结点不同，返回false
        if (p.val != q.val) {
            return false;
        }

        //根结点相同，判断左右子树是否相同
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }



    // 判断subRoot 是不是 root 的子树
    // 是不是相同的树
    // 是不是root的左子树
    // 是不是root的右子树
    // 时间复杂度 O(m*n)   空间复杂度 0(m*n)
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if (isSameTree(root, subRoot)) {
            //两树相同
            return true;
        }
        if (root == null || subRoot == null) {
            return false;
        }
        if (isSubtree(root.left, subRoot)) {
            return true;
        }
        if (isSubtree(root.right, subRoot)) {
            return true;
        }
        return false;
    }



    // 翻转二叉树
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }

        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;

        invertTree(root.left);
        invertTree(root.right);

        return root;
    }



    // 对称二叉树
    // 1. 翻转右子树,再比较左右子树是否相同
    public boolean isSymmetric1(TreeNode root) {
        if (root == null) {
            return true;
        }
        invertTree(root.right);
        return isSameTree(root.left,root.right);
    }

    // 2. 递归比较
    public boolean isSymmetric2(TreeNode root) {
        if (root == null) {
            return true;
        }
        return isSymmetricChild(root.left, root.right);
    }
    public boolean isSymmetricChild(TreeNode leftTree, TreeNode rightTree) {
        // 两个都空，true
        if (leftTree == null && rightTree == null) {
            return true;
        }
        // 一个空，一个不空，结构不同，不对称，false
        if (rightTree == null || leftTree == null) {
            return false;
        }
        // 两个都不空，判断值是否相同
        if (leftTree.val != rightTree.val) {
            return false;
        }

        // 值相同，继续递归
        // 左子树的左边，与右子树的右边
        // 左子树的右边，与右子树的左边
        return isSymmetricChild(leftTree.left, rightTree.right)
                && isSymmetricChild(leftTree.right, rightTree.left);
    }



    // 平衡二叉树

    // 1
    // 重复计算高度了
    // O(n^2)
    // 每个结点都去调用getHeight 计算高度
    public boolean isBalanced1(TreeNode root) {
        if (root == null) {
            return true;
        }
        int left = getHeight(root.left);
        int right = getHeight(root.right);
        // 左右子树相差高度不超过1
        if (Math.abs(left - right) > 1) {
            return false;
        }
        // 判断左右子树
        if (!isBalanced1(root.left) || !isBalanced1(root.right)) {
            return false;
        }
        return true;
    }

   // 2
    public boolean isBalanced2(TreeNode root) {
        // 计算一次高度
        return getHeight2(root) >= 0;
    }
    public int getHeight2(TreeNode root) {
        if (root == null) {
            return 0;
        }

        int leftHeight = getHeight2(root.left);
        if (leftHeight < 0) return -1;
        int rightHeight = getHeight2(root.right);
        if (rightHeight < 0) return -1;

        if (Math.abs(leftHeight - rightHeight) <= 1) {
            return Math.max(leftHeight,rightHeight) + 1;
        } else {
            return -1;
        }
    }



    // 根据前序遍历 创建二叉树 ABC##DE#G##F###
    public static int i = 0;
    public TreeNode createTree(String str) {
        TreeNode root = null;
        if (str.charAt(i) != '#') {
            root = new TreeNode(str.charAt(i));
            i++;
            root.left = createTree(str);
            root.right = createTree(str);
        } else {
            i++;
        }
        return root;
    }



    // 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先

    // 1
    public TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        //当前结点为 p 或 q 返回
        if (root == p || root == q) {
            return root;
        }

        // 找左右两边
        TreeNode left = lowestCommonAncestor1(root.left, p , q);
        TreeNode right = lowestCommonAncestor1(root.right, p , q);

        // 提示 p 和 q 均存在于给定的二叉树中。

        // 左右都不空 当前根结点 是公共祖先
        // 一边空， 一边不空，公共祖先都在不空的一边
        if (left != null && right != null) {
            return root;
        } else if (left != null) {
            return left;
        } else if (right != null) {
            return right;
        }
        return null;
    }

    // 2
    // 先将两个结点到根结点的路径结点分别放入两个栈中
    // 再将结点多的栈中弹出结点，让两个栈结点相同
    // 然后让两个栈分别弹出结点比较，找到相同结点，就是公共祖先
    public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
        // 创建两个栈
        Deque<TreeNode> stack1 = new LinkedList<>();
        Deque<TreeNode> stack2 = new LinkedList<>();
        // 两个栈分别存放路径
        getPath(root, p, stack1);
        getPath(root, q, stack2);

        int size1 = stack1.size();
        int size2 = stack2.size();

        // 把栈中结点多的 弹出，保证两个栈结点一样多
        if (size1 > size2) {
            for (int i = 0; i < size1 - size2; i++) {
                stack1.pop();
            }
        } else if (size1 < size2) {
            for (int i = 0; i < size2 - size1; i++) {
                stack2.pop();
            }
        }

        // 然后都两个栈各弹出结点 比较
        while (!stack1.isEmpty()) {
            if (stack1.peek() == stack2.peek()) {
                // 找到公共祖先，返回该结点
                return stack1.peek();
            }
            stack1.pop();
            stack2.pop();
        }

        return null;
    }
    // 找到 根结点 到 node 结点路径上所有的结点，放入栈中
    public boolean getPath(TreeNode root, TreeNode node, Deque<TreeNode> stack) {
        if (root == null ||node == null) {
            return false;
        }
        // 将当前结点放入栈中
        stack.push(root);

        // 找到了指定结点 返回true
        if (root == node) {
            return true;
        }

        // 找左右两边
        boolean left = getPath(root.left, node, stack);
        if (left == true) {
            return true;
        }
        boolean right = getPath(root.right, node, stack);
        if (right == true) {
            return true;
        }

        // 两边都没找到，说明当前结点不是路径上的结点
        // 弹出当前结点
        stack.pop();
        return false;
    }



    // 根据一棵树的前序遍历与中序遍历构造二叉树
    public int preIndex = 0;
    public TreeNode buildTree1(char[] preorder, char[] inorder) {
        return buildTreeChild1(preorder, inorder, 0, inorder.length - 1);
    }
    private TreeNode buildTreeChild1(char[] preorder, char[] inorder, int inBegin, int inEnd) {
        // 如果inBegin > inEnd
        // 说明这里没子树了
        if (inBegin > inEnd) {
            return null;
        }

        // 创建根结点
        TreeNode root = new TreeNode(preorder[preIndex]);

        // 找到当前根，在中序遍历中的位置
        int rootIndex = findIndex(inorder, inBegin, inEnd, preorder[preIndex]);

        // 前序遍历下标加1
        preIndex++;

        // 当前根的左孩子
        root.left = buildTreeChild1(preorder, inorder, inBegin, rootIndex - 1);
        // 当前根的右孩子
        root.right = buildTreeChild1(preorder, inorder, rootIndex + 1, inEnd);

        return root;
    }
    // 找到当前结点，在中序遍历里的位置，确定左右子树
    private int findIndex(char[] inorder, int inBegin, int inEnd, char val) {
        for (int i = inBegin; i <= inEnd; i++) {
            if (inorder[i] == val) {
                return i;
            }
        }
        return -1;
    }


    // 根据一棵树的后序遍历与中序遍历构造二叉树
    public int postIndex = 0;
    public TreeNode buildTree2(char[] inorder, char[] postorder) {
        postIndex = postorder.length - 1;
        return buildTreeChild2(postorder, inorder, 0, inorder.length - 1);
    }
    private TreeNode buildTreeChild2(char[] postorder, char[] inorder, int inBegin, int inEnd) {
        // 如果inBegin > inEnd
        // 说明这里没子树了
        if (inBegin > inEnd) {
            return null;
        }

        // 创建根结点
        TreeNode root = new TreeNode(postorder[postIndex]);

        // 找到当前根，在中序遍历中的位置
        int rootIndex = findIndex(inorder, inBegin, inEnd, postorder[postIndex]);

        // 后序遍历下标加1
        postIndex--;

        // 根据后序遍历，从后往前，根右左
        // 当前根的右孩子
        root.right = buildTreeChild2(postorder, inorder, rootIndex + 1, inEnd);
        // 当前根的左孩子
        root.left = buildTreeChild2(postorder, inorder, inBegin, rootIndex - 1);

        return root;
    }



    // 根据二叉树创建字符串
    public String tree2str(TreeNode root) {
        // 只创建一个StringBuilder 引用
        StringBuilder s = new StringBuilder();
        getStr(root, s);
        return s.toString();
    }
    public void getStr(TreeNode root, StringBuilder s) {
        if (root == null) {
            return;
        }

        // 把根结点放入
        s.append(root.val);

        // 左边不空，先加一个 '('
        // 再递归左子树
        // 最后加上 ')'
        if (root.left != null) {
            s.append('(');
            getStr(root.left, s);
            s.append(')');
        }

        // 右边不空
        // 左边空，左边不空
        // 两种情况
        if (root.right != null) {
            // 右边不空，但是左边为空，加一个 "()"
            if (root.left == null) {
                s.append("()");
            }

            s.append('(');
            getStr(root.right, s);
            s.append(')');
        }

    }

    // 每次递归都创建一个StringBuilder引用
    // 速度较慢
    public String tree1str(TreeNode root) {
        if (root == null) {
            return null;
        }

        StringBuilder s = new StringBuilder();
        s.append(root.val);

        // 递归左右子树
        String left = tree1str(root.left);
        String right = tree1str(root.right);

        // 左不空
        if (left != null) {
            s.append('(');
            s.append(left);
            s.append(')');
        }

        // 右不空
        // 左空 或 不空
        if (right != null) {
            if (left == null) {
                s.append("()");
            }
            s.append('(');
            s.append(right);
            s.append(')');
        }

        return s.toString();
    }

}
